package com.ai.sml.action;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;

import com.ai.common.xml.util.ControlConstants;
import com.ai.common.xml.util.ControlConstants.RETURN_CODE;
import com.ai.frame.bean.InputObject;
import com.ai.frame.bean.OutputObject;
import com.ai.frame.logger.Logger;
import com.ai.frame.logger.LoggerFactory;
import com.ai.sml.convertor.OutputObjectConvertor;
import com.ai.sml.util.CacheUtil;
import com.ai.sml.util.DateUtil;
import com.ai.sml.util.DownloadExcelUtil;
import com.ai.sml.util.ExcelPOI07Util;
import com.ai.sml.util.PropertiesUtil;
import com.ai.sml.util.SFtpUtil;
import com.ai.sml.util.StringUtil;
import com.ai.sml.util.WebUtils;
import com.ai.sml.util.Constants.SFTP_CONFIG;

/**
 * 
 * 
 */
public class CommonAction extends BaseAction {
	private static final long serialVersionUID = 2021751417577646314L;
	private static final Logger logger = LoggerFactory.getActionLog(CommonAction.class);
	
	/**
	 * excel 导出到ftp时参数定义
	 * @param 
	 * 		pageSize:分页查询时的分页长度
	 * 		sheetSize：生成excel时每个sheet的长度
	 * 		changeDateLength:生成到ftp的临界值，当大于20000的时候，生成excel到ftp,否则生成到本地
	 * @author 贾刚磊
	 * @version 创建时间：2016年11月17日11:48:23
	 *
	 */
	public interface UPLOADEPARAM{
		Integer pageSize = 100;
		Integer sheetSize = 50000;
		//Integer MaxDataLength = 100000;
	}
	/** Uniform Method Invocation **/
	public String execute() {
		logger.info("execute", "Start");
		OutputObject object = super.getOutputObject();
		super.sendJson(super.convertOutputObject2Json(object));
		logger.info("execute", "End");
		return null;
	}

	// 发送验证码
	public void sendValidateCode() {
		logger.info("sendValidateCode", "Start");

		InputObject inputObject = getInputObject();

		String phone = inputObject.getParams().get("phone");
		String ramCode = WebUtils.createRandom(true, 6);
		inputObject.getParams().put("ramCode", ramCode);
		System.out.println(ramCode);

		CacheUtil.put2Cache(phone, ramCode, 15 * 60);

		OutputObject object = super.getOutputObject(inputObject);
		super.sendJson(super.convertOutputObject2Json(object));

		logger.info("sendValidateCode", "End");
	}

	/**
	 * 根据当前客户获取客户对象
	 * 
	 * @return Map
	 */
	protected Map<String, String> getCustomerObject() {
		String userLoginId = (String) SecurityUtils.getSubject().getPrincipal();
		InputObject inObject = new InputObject();
		inObject.setService("orderHelperService");
		inObject.setMethod("getCustomer");
		String custId = (String) SecurityUtils.getSubject().getSession().getAttribute("custId");
		inObject.getParams().put("custId", custId);
		inObject.getParams().put("userLoginId", userLoginId);
		OutputObject outObject = super.getOutputObject(inObject);
		Map<String, String> bean = outObject.getBean();
		if (RETURN_CODE.IS_OK.equals(outObject.getReturnCode())) {
			List<Map<String, String>> beans = outObject.getBeans();
			String areas = "";
			if(!beans.isEmpty()){
				for(Map<String, String> area : beans){
					areas+=area.get("regnCd");
					areas+=",";
				}
				areas.substring(0, areas.length()-2);
			}
			bean.put("areas", areas);
			return bean;
		}
		return null;
	}

	protected String getFtpUrl() {
		String visitUrl = WebUtils.getCurrentIP(getRequest());
		String ftpUrl;
		if (StringUtil.isNotEmpty(visitUrl) && visitUrl.length() > 3) {
			if (visitUrl.contains("10.") || visitUrl.contains("192.168.") || visitUrl.contains("localhost")) {
				ftpUrl = PropertiesUtil.getString(SFTP_CONFIG.SFTP_PREFIX_IN);
			} else {
				ftpUrl = PropertiesUtil.getString(SFTP_CONFIG.SFTP_PREFIX_OUT);
			}
		} else {
			ftpUrl = PropertiesUtil.getString(SFTP_CONFIG.SFTP_PREFIX_OUT);
		}
		return ftpUrl;
	}
	
	protected void exportWebExcel(OutputObject obj) {
		logger.info("exportWebExcel", "start");
		if (ControlConstants.RETURN_CODE.SYSTEM_ERROR.equals(obj.getReturnCode())) {
			super.sendJson(super.convertOutputObject2Json(obj));
			return;
		}

		HttpServletResponse resp = getResponse();
		OutputStream outStream = null;
		try {
			outStream = resp.getOutputStream();
			resp.reset();
			String fileName = obj.getBean().get("fileName");
			if (StringUtil.isEmpty(fileName)) {
				fileName = "导出订单_" + System.currentTimeMillis() + ".xlsx";
			}

			String extStr = fileName.substring(fileName.indexOf(".") + 1);
			if ("xls".equalsIgnoreCase(extStr)) {
				resp.setContentType("application/vnd.ms-excel");
			} else {
				resp.setContentType("application/octet-stream");
			}
			setResponseFileName(resp, fileName);
			ExcelPOI07Util.createExcel2007(outStream, obj);
			obj.getBeans().clear();
			outStream.close();
			resp.flushBuffer();
		} catch (Exception e) {
			logger.error("exportWebExcel", "导出文件异常", e);
			super.sendJson("{\"returnCode\":\"-9999\",\"returnMessage\":\"导出文件失败\"}");
		}
		logger.info("exportWebExcel", "end");
	}
	
	public void setResponseFileName(HttpServletResponse response, String fileName) throws UnsupportedEncodingException  {
		HttpServletRequest req = getRequest();
		String userAgent = req.getHeader("User-Agent");
		boolean isIE = false;
		if (userAgent != null
				&& (userAgent.toLowerCase().contains("msie") || userAgent.toLowerCase().contains("rv"))) {
			isIE = true;
		}
		String displayName;
		if (isIE) {
			displayName = URLEncoder.encode(fileName, "UTF-8");
			displayName = displayName.replaceAll("\\+", "%20");// 修正URLEncoder将空格转换成+号的BUG
			response.setHeader("Content-Disposition", "attachment;filename=" + displayName);
		} else {
			displayName = new String(fileName.getBytes("UTF-8"), "ISO8859-1");
			response.setHeader("Content-Disposition", "attachment;filename=\"" + displayName + "\"");// firefox空格截断
		}
		String extStr = displayName.substring(displayName.indexOf(".") + 1);
		if ("xls".equalsIgnoreCase(extStr)) {
			response.setContentType("application/vnd.ms-excel");
		} else {
			response.setContentType("application/octet-stream");
		}
	}
	
	/**
	 * 批量导出(最多支持6W行)
	 * @param inputObject 查询数据参数
	 * @param total 总计录数
	 * @param fileName 文件名
	 * @param sheetName 表单名
	 * @param headCnName 列表头
	 * @param dataEnName 对应字段名
	 * @param convert 导出数据转换
	 */
	protected void batchExportExcel2(final InputObject inputObject, final Integer total, final String fileName,
			final String sheetName, String headCnName, final String dataEnName, final OutputObjectConvertor convert) {
		ServletOutputStream outputStream = null;
		HttpServletResponse response = getResponse();
		try {
			response.reset();
			setResponseFileName(response, fileName);
			outputStream = response.getOutputStream();
			final Integer limit = 1000;
			Integer size = Math.min(60000, total);
			Integer pages = (size - 1) / limit + 1;
			inputObject.getParams().put("limit", limit + "");
			final DownloadExcelUtil excelUtil = new DownloadExcelUtil(outputStream, sheetName);
			excelUtil.setTitles(headCnName.split(","));
			ExecutorService executorService = Executors.newFixedThreadPool(2);
			for (int i = 0; i < pages; i++) {
				final int index = i;
				executorService.execute(new Runnable() {
					public void run() {
						InputObject input = new InputObject();
						input.setBusiCode(inputObject.getBusiCode());
						input.setService(inputObject.getService());
						input.setMethod(inputObject.getMethod());
						input.getParams().putAll(inputObject.getParams());
						input.getParams().put("start", Integer.toString(limit * index));
						OutputObject outputObject = getOutputObject(input);
						if (RETURN_CODE.IS_OK.equals(outputObject.getReturnCode())) {
							convert.convertor(outputObject);
							try {
								excelUtil.addRows(index * limit, outputObject.getBeans(), dataEnName.split(","), null);
							} catch (IOException e) {
								logger.error("XXXXXXXXX", "导出文件出错", e);
							}
						}
					}
				});
			}
			executorService.shutdown();
			try {
				executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
			} catch (InterruptedException e) {
				logger.error("XXXXXXXXX", "awaitTermination", e);
			}
			excelUtil.reportExcel();
		} catch (Exception e) {
			logger.error("XXXXXXXXX", "导出文件出错", e);
			OutputObject outputObject = new OutputObject(RETURN_CODE.SYSTEM_ERROR, e.getMessage());
			try {
				response.reset();
				response.setContentType("text/html");
				if(outputStream != null) {
					outputStream.print(convertOutputObject2Json(outputObject));
				}
			} catch (IOException e1) {
				logger.error("XXXXXXXXX", "导出文件出错", e1);
			}
		}
	}

	/** 获取系统参数 */
	protected String getSysParam(String key) {
		InputObject inputObject = new InputObject();
		inputObject.setService("flowParamService");
		inputObject.setMethod("queryByKey");
		inputObject.getParams().put("paramKey", key);
		OutputObject outputObject = getOutputObject(inputObject);
		return outputObject.getBean().get("paramValue");
	}
	
	public boolean lock(String key) {
		synchronized (logger) {
			if (StringUtils.isBlank(CacheUtil.getFromCache(key))) {
				if (CacheUtil.setnx(key, String.valueOf(System.currentTimeMillis()))) {
					return true;
				}
			}
			int expires = 1000 * 60 * 60 * 2;
			String currentValue = CacheUtil.getFromCache(key);
			if (currentValue != null && Long.parseLong(currentValue) < System.currentTimeMillis() - expires) {
				CacheUtil.put2Cache(key, String.valueOf(System.currentTimeMillis()), expires);
				return true;
			}
			return false;
		}
	}
	
	/**
	 * 批量导出(最多支持1W行)
	 * @param inputObject 查询数据参数
	 * @param total 总计录数
	 * @param fileName 文件名
	 * @param sheetName 表单名
	 * @param headCnName 列表头
	 * @param dataEnName 对应字段名
	 * @param convert 导出数据转换
	 */
	protected void batchExportExcel(final InputObject inputObject, final Integer total, final String fileName,
			final String sheetName, String headCnName, final String dataEnName, final OutputObjectConvertor convert) {
		String className = getClass().getSimpleName();
		if (lock(className)) {
			Integer exportDataMax = Integer.parseInt(StringUtils.defaultIfBlank(getSysParam("EXPORT_DATA_MAX"), "100000"));
			Integer onlineMax = Integer.parseInt(StringUtils.defaultIfBlank(getSysParam("EXPORT_ONLINE_MAX"), "10000"));
			try {
				if (total <= onlineMax) {
					exportThisTime(inputObject, total, fileName, sheetName, headCnName, dataEnName, convert);
				} else if (total > exportDataMax) {
					OutputObject outputObject = new OutputObject(RETURN_CODE.SYSTEM_ERROR,
							"导出数据最多支持" + exportDataMax + "条,请更换导出条件");
					sendJson(convertOutputObject2Json(outputObject));
				} else {
					String dirName = (String) SecurityUtils.getSubject().getPrincipal();
					except2Ftp(inputObject, total, fileName, sheetName, headCnName, dataEnName, dirName, convert);
				}
			} finally {
				if (total <= onlineMax || total > exportDataMax) {
					CacheUtil.delFromCache(className);
				}
			}
		} else {
			OutputObject outputObject = new OutputObject(RETURN_CODE.SYSTEM_ERROR, "当前操作人数过多，请稍后再试");
			sendJson(convertOutputObject2Json(outputObject));
		}
	}

	/**
	 * 生成及时的excel文档
	 * @param inputObject
	 * @param total
	 * @param fileName
	 * @param sheetName
	 * @param headCnName
	 * @param dataEnName
	 * @param convert
	 */
	private void exportThisTime(final InputObject inputObject, final Integer total, final String fileName,
			final String sheetName, String headCnName, final String dataEnName, final OutputObjectConvertor convert) {
		ServletOutputStream outputStream = null;
		HttpServletResponse response = getResponse();
		try {
			response.reset();
			setResponseFileName(response, fileName);
			outputStream = response.getOutputStream();
			final Integer limit = UPLOADEPARAM.pageSize;
			Integer pages = (total - 1) / limit + 1;
			inputObject.getParams().put("limit", limit + "");
			final DownloadExcelUtil excelUtil = new DownloadExcelUtil(outputStream, sheetName);
			excelUtil.setTitles(headCnName.split(","));
			ExecutorService executorService = Executors.newFixedThreadPool(5);
			for (int i = 0; i < pages; i++) {
				final int index = i;
				executorService.execute(new Runnable() {
					public void run() {
						InputObject input = new InputObject();
						input.setBusiCode(inputObject.getBusiCode());
						input.setService(inputObject.getService());
						input.setMethod(inputObject.getMethod());
						input.getParams().putAll(inputObject.getParams());
						input.getParams().put("start", Integer.toString(limit * index));
						OutputObject outputObject = getOutputObject(input);
						if (RETURN_CODE.IS_OK.equals(outputObject.getReturnCode())) {
							convert.convertor(outputObject);
							try {
								excelUtil.addRows(index * limit, outputObject.getBeans(), dataEnName.split(","), null);
							} catch (IOException e) {
								logger.error("XXXXXXXXX", "导出文件出错", e);
							}
						}
					}
				});
			}
			executorService.shutdown();
			try {
				executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
			} catch (InterruptedException e) {
				logger.error("XXXXXXXXX", "awaitTermination", e);
			}
			excelUtil.reportExcel();
		} catch (Exception e) {
			logger.error("XXXXXXXXX", "导出文件出错", e);
			OutputObject outputObject = new OutputObject(RETURN_CODE.SYSTEM_ERROR, "导出文件出错");
			try {
				response.reset();
				response.setContentType("text/html");
				if (outputStream != null) {
					outputStream.print(convertOutputObject2Json(outputObject));
				}
			} catch (IOException e1) {
				logger.error("XXXXXXXXX", "导出文件出错", e1);
			}
		}
	}

	transient ExecutorService executorServiceMain = Executors.newFixedThreadPool(1);
	/**
	 * 导出到ftp服务器
	 * @param inputObject
	 * @param total
	 * @param fileName
	 * @param sheetName
	 * @param headCnName
	 * @param dataEnName
	 * @param dirName
	 * @param convert
	 */
	private void except2Ftp(final InputObject inputObject, final Integer total, final String fileName,
			final String sheetName, final String headCnName, final String dataEnName,final String dirName,
			final OutputObjectConvertor convert) {
		final String remotePath = PropertiesUtil.getString(SFTP_CONFIG.SFTP_UPLOAD_MCDS_NGINX) + "/" + dirName;
		OutputObject outputObject = new OutputObject(RETURN_CODE.IS_OK, "数据太多，请稍后到指定页面下载");
		final String rootPath = getClass().getResource("/").getFile() + fileName;
		String remoteFileName = remotePath + "/" + fileName;
		String ftpUrl = getFtpUrl() + remoteFileName;
		Map<String, String> map = new HashMap<String, String>();
		map.put("ftpUrl", ftpUrl);
		outputObject.setBean(map);
		sendJson(convertOutputObject2Json(outputObject));

		final String className = getClass().getSimpleName();
		executorServiceMain.execute(new Runnable() {
			@Override
			public void run() {
				OutputStream outputStream = null;
				InputStream is = null;
				File file = new File(rootPath);
				try {
					if (!file.getParentFile().exists()) {
						file.getParentFile().mkdirs();
					}
					outputStream = new FileOutputStream(rootPath);
					final Integer limit = UPLOADEPARAM.pageSize;
					Integer size = total;// Math.min(60000, total);
					Integer pages = (size - 1) / limit + 1;
					inputObject.getParams().put("limit", limit + "");
					final DownloadExcelUtil excelUtil = new DownloadExcelUtil(outputStream, sheetName);
					excelUtil.setTitles(headCnName.split(","));
					final int sheetSize = UPLOADEPARAM.sheetSize;
					int sheets = size / sheetSize;
					if (size % sheetSize > 0) {
						sheets++;
					}
					for (int i = 1; i < sheets; i++) {
						excelUtil.addSheet(sheetName);
						excelUtil.setColumnView();
						excelUtil.setTitles(headCnName.split(","));
					}

					ExecutorService executorService = Executors.newFixedThreadPool(5);
					for (int i = 0; i < pages; i++) {
						try{
						    Thread.sleep(200);
						}catch (InterruptedException e){
							logger.error("线程休眠", e.getMessage(), e);
						}
						
						final int index = i;
						executorService.execute(new Runnable() {
							public void run() {
								InputObject input = new InputObject();
								input.setBusiCode("ORDER");
								input.setService("traffic");
								input.setMethod("orders");
								input.getParams().put("scope", "GET");
								input.getParams().putAll(inputObject.getParams());
								input.getParams().put("start", Integer.toString(limit * index));
								OutputObject outputObject = new OutputObject();
								try {
									 outputObject = getOutputObject(input);
								} catch (Exception e) {
									logger.error("获取订单信息失败", "获取订单信息失败",e);
								}
								if (RETURN_CODE.IS_OK.equals(outputObject.getReturnCode())) {
									convert.convertor(outputObject);
									try {
										excelUtil.addRows(index * limit, outputObject.getBeans(), dataEnName.split(","),
												null, sheetSize);
									} catch (IOException e) {
										logger.error("XXXXXXXXX", "导出文件出错", e);
									}
								}
							}
						});
					}
					executorService.shutdown();
					try {
						executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
					} catch (InterruptedException e) {
						logger.error("XXXXXXXXX", "awaitTermination", e);
					}
					excelUtil.reportExcel();
					file = new File(rootPath);
					is = new FileInputStream(file);
					String excelftpUrl = SFtpUtil.uploadFile2Ftp(remotePath, fileName, is);
					logger.info("文件导出链接:", excelftpUrl);

					Map<String, String> inParam = new HashMap<String,String>();
					inParam.put("excSize", convertFileSize(file.length()));
					inParam.put("custId",dirName);
					inParam.put("name",fileName);
					inParam.put("status","2");
					inParam.put("dirc",remotePath);
					inParam.put("createTime",DateUtil.date2String(new Date(),DateUtil.DATE_PATTERN.YYYY_MM_DD_HH_MM_SS ));
					//插入上传记录
					InputObject insertToLocation = new InputObject();
					insertToLocation.setService("orderTaskFtpService");
					insertToLocation.setMethod("insertOrderTaskFtpItem");
					insertToLocation.setParams(inParam);
					getOutputObject(insertToLocation);
				} catch (Exception e) {
					logger.error("Exception", "导出文件出错", e);
				}finally{
					CacheUtil.delFromCache(className);
					try {
						if (is != null) {
							is.close();
						}
					} catch (IOException e) {
						logger.error("IOException", e.getMessage(), e);
					}
					try {
						if (outputStream != null) {
							outputStream.close();
						}
					} catch (IOException e) {
						logger.error("IOException", e.getMessage(), e);
					}
					// 路径为文件且不为空则进行删除
					if (file.isFile() && file.exists()) {
						file.delete();
					}
				}
			}
		});
	}
	
	/**
	 * 文件大小转换为M，G,K
	 * @param size
	 * @return
	 */
	private String convertFileSize(long size) {
        long kb = 1024;
        long mb = kb * 1024;
        long gb = mb * 1024;
 
        if (size >= gb) {
            return String.format("%.1f GB", (float) size / gb);
        } else if (size >= mb) {
            float f = (float) size / mb;
            return String.format(f > 100 ? "%.0f MB" : "%.1f MB", f);
        } else if (size >= kb) {
            float f = (float) size / kb;
            return String.format(f > 100 ? "%.0f KB" : "%.1f KB", f);
        } else
            return String.format("%d B", size);
    }
}
